import { fabric } from "fabric";
import { Point } from "fabric/fabric-impl";
import { Vector2 } from "three";
export function polygonPositionHandler(
  this: any,
  dim,
  finalMatrix,
  fabricObject
) {
  const x = fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
    y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
  return fabric.util.transformPoint(
    { x: x, y: y } as Point,
    fabric.util.multiplyTransformMatrices(
      fabricObject.canvas.viewportTransform,
      fabricObject.calcTransformMatrix()
    )
  );
}

export function anchorWrapper(anchorIndex, fn) {
  return function (eventData, transform, x, y) {
    const fabricObject = transform.target,
      absolutePoint = fabric.util.transformPoint(
        {
          x: fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
          y: fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y,
        } as Point,
        fabricObject.calcTransformMatrix()
      ),
      actionPerformed = fn(eventData, transform, x, y),
      newDim = fabricObject._setPositionDimensions({}),
      polygonBaseSize = getObjectSizeWithStroke(fabricObject),
      newX =
        (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) /
        polygonBaseSize.x,
      newY =
        (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) /
        polygonBaseSize.y;
    fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
    return actionPerformed;
  };
}
export function actionHandler(eventData, transform, x, y) {
  const polygon = transform.target,
    currentControl = polygon.controls[polygon.__corner],
    mouseLocalPosition = polygon.toLocalPoint(
      new fabric.Point(x, y),
      "center",
      "center"
    ),
    polygonBaseSize = getObjectSizeWithStroke(polygon),
    size = polygon._getTransformedDimensions(0, 0),
    finalPointPosition = {
      x:
        (mouseLocalPosition.x * polygonBaseSize.x) / size.x +
        polygon.pathOffset.x,
      y:
        (mouseLocalPosition.y * polygonBaseSize.y) / size.y +
        polygon.pathOffset.y,
    };
  polygon.points[currentControl.pointIndex] = finalPointPosition;
  return true;
}
function getObjectSizeWithStroke(object) {
  const stroke = new fabric.Point(
    object.strokeUniform ? 1 / object.scaleX : 1,
    object.strokeUniform ? 1 / object.scaleY : 1
  ).multiply(object.strokeWidth);
  return new fabric.Point(object.width + stroke.x, object.height + stroke.y);
}

export function isIntersect(line1, line2) {
  // 转换成一般式: Ax+By = C
  const a1 = line1.y2 - line1.y1;
  const b1 = line1.x1 - line1.x2;
  const c1 = a1 * line1.x1 + b1 * line1.y1;

  //转换成一般式: Ax+By = C
  const a2 = line2.y2 - line2.y1;
  const b2 = line2.x1 - line2.x2;
  const c2 = a2 * line2.x1 + b2 * line2.y1;

  // 计算交点
  const d = a1 * b2 - a2 * b1;
  // 当d==0时，两线平行
  if (d == 0) {
    return false;
  } else {
    const x = (b2 * c1 - b1 * c2) / d;
    const y = (a1 * c2 - a2 * c1) / d;

    // 检测交点是否在两条线段上
    if (
      (isInBetween(line1.x1, x, line1.x2) ||
        isInBetween(line1.y1, y, line1.y2)) &&
      (isInBetween(line2.x1, x, line2.x2) || isInBetween(line2.y1, y, line2.y2))
    ) {
      return true;
    }
  }

  return false;
}

function isInBetween(a, b, c) {
  // 如果b几乎等于a或c，返回false.为了避免浮点运行时两值几乎相等，但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
    return false;
  }

  return (a < b && b < c) || (c < b && b < a);
}
export function segmentsIntr(a: Vector2, b: Vector2, c: Vector2, d: Vector2) {
  // 三角形abc 面积的2倍
  const area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);

  // 三角形abd 面积的2倍
  const area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);

  // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);
  if (area_abc * area_abd >= 0) {
    return false;
  }

  // 三角形cda 面积的2倍
  const area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
  // 三角形cdb 面积的2倍
  // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.
  const area_cdb = area_cda + area_abc - area_abd;
  if (area_cda * area_cdb >= 0) {
    return false;
  }

  //计算交点坐标
  const t = area_cda / (area_abd - area_abc);
  const dx = t * (b.x - a.x),
    dy = t * (b.y - a.y);
  return { x: a.x + dx, y: a.y + dy };
}

//求（x,y）到直线的带符号距离
export function getSignedDistance(
  a: number,
  b: number,
  c: number,
  x: number,
  y: number
) {
  let d = (a * x + b * y + c) / Math.sqrt(a * a + b * b);
  d = Math.sqrt(d * d); //取d的正数
  if (b == 0) {
    //当直线垂直于x轴时，直线的表达式可表示为：x = -c/a
    if (x <= -c / a) return -d; //点位于直线左侧，符号取负
    else return d; //点位于直线左侧，符号取正
  } else {
    if (y >= (-a / b) * x - c / b) return d; //点位于直线上侧，符号取正
    else return -d; //点位于直线下侧，符号取负
  }
}
//直线与线段交点
export function pointLineSegment(
  a: number,
  b: number,
  c: number,
  x1: number,
  y1: number,
  x2: number,
  y2: number
) {
  let d1 = 0,
    d2 = 0;
  d1 = getSignedDistance(a, b, c, x1, y1);
  d2 = getSignedDistance(a, b, c, x2, y2);
  if (d1 * d2 <= 0) {
    //当d1与d2异号或者其中至少有一个为0时，交点存在
    d1 = Math.sqrt(d1 * d1); //去符号
    d2 = Math.sqrt(d2 * d2);
    let k = d1 / (d1 + d2); //求d1所占线段的比例
    if (
      d1 + d2 != 0 &&
      (x1 + k * (x2 - x1) != x1 || y1 + k * (y2 - y1) != y1)
    ) {
      //距离都不为0且不是线段的第一个端点时（为了避免交点为矩形邻边的交点导致端点被计算两次的情况）
      console.log(
        "直线与矩形的交点x:",
        x1 + k * (x2 - x1),
        "y:",
        y1 + k * (y2 - y1)
      );
      return { x: x1 + k * (x2 - x1), y: y1 + k * (y2 - y1) };
    } else if (d1 + d2 == 0) {
      console.log("直线与矩形的一条边重合，存在无数个交点");
      return false;
    }
  } else {
    return false;
  }
  return false;
}
//直线与矩形交点
export function pointLineRectangular(
  beginPoint: Vector2,
  endPoint: Vector2,
  lefttop: Vector2,
  righttop: Vector2,
  rightbottom: Vector2,
  leftbottom: Vector2
) {
  //接下来依次求解直线与构成矩形的四条线段的交点
  let distancem1 = 9999999,
    distancem2 = 9999999,
    distancem3 = 9999999,
    distancem4 = 9999999;
  let ax = (endPoint.y - beginPoint.y) / (endPoint.x - beginPoint.x);
  let cd = beginPoint.y - ax * beginPoint.x;
  let by = -1;
  const m1 = pointLineSegment(
    ax,
    by,
    cd,
    lefttop.x,
    lefttop.y,
    righttop.x,
    righttop.y
  );
  const m2 = pointLineSegment(
    ax,
    by,
    cd,
    righttop.x,
    righttop.y,
    rightbottom.x,
    rightbottom.y
  );
  const m3 = pointLineSegment(
    ax,
    by,
    cd,
    rightbottom.x,
    rightbottom.y,
    leftbottom.x,
    leftbottom.y
  );
  const m4 = pointLineSegment(
    ax,
    by,
    cd,
    leftbottom.x,
    leftbottom.y,
    lefttop.x,
    lefttop.y
  );
  if (!m1 && !m2 && !m3 && !m4) {
    return false;
  } else {
    if (m1) {
      distancem1 =
        Math.pow(beginPoint.y - m1.y, 2) + Math.pow(beginPoint.x - m1.x, 2);
    }
    if (m2) {
      distancem2 =
        Math.pow(beginPoint.y - m2.y, 2) + Math.pow(beginPoint.x - m2.x, 2);
    }
    if (m3) {
      distancem3 =
        Math.pow(beginPoint.y - m3.y, 2) + Math.pow(beginPoint.x - m3.x, 2);
    }
    if (m4) {
      distancem4 =
        Math.pow(beginPoint.y - m4.y, 2) + Math.pow(beginPoint.x - m4.x, 2);
    }
    const array = [distancem1, distancem2, distancem3, distancem4];
    const resut = [m1, m2, m3, m4];
    const mindistance = Math.min(...array);
    const index = array.indexOf(mindistance);
    return index > -1 ? resut[index] : false;
  }
}
